#include "IpAddress.hpp"

using namespace std;



const Network::IpAddress Network::IpAddress::ipv4_any("0.0.0.0");
const Network::IpAddress Network::IpAddress::ipv4_localhost("127.0.0.1");
const Network::IpAddress Network::IpAddress::ipv6_any("::");
const Network::IpAddress Network::IpAddress::ipv6_localhost("::1");



#ifdef _WIN32
	const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt)
	{
		if(af == AF_INET)
		{
			SOCKADDR_IN in;
			
			memset(&in, 0, sizeof(in));
			in.sin_family = AF_INET;
			memcpy(&in.sin_addr, src, sizeof(struct in_addr));
			getnameinfo((SOCKADDR*)&in, sizeof(in), dst, cnt, NULL, 0, NI_NUMERICHOST);
			
			return dst;
		}
		else if(af == AF_INET6)
		{
			SOCKADDR_IN6 in;
			
			memset(&in, 0, sizeof(in));
			in.sin6_family = AF_INET6;
			memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
			getnameinfo((SOCKADDR*)&in, sizeof(in), dst, cnt, NULL, 0, NI_NUMERICHOST);
			
			return dst;
		}
		
		return NULL;
	}

	int inet_pton(int af, const char* src, void* dst)
	{
		struct addrinfo hints, *res, *ressave;

		memset(&hints, 0, sizeof(struct addrinfo));
		hints.ai_family = af;

		if(getaddrinfo(src, NULL, &hints, &res) != 0)
		{
			//cerr << "Couldn't resolve host %s\n";
			return -1;
		}

		ressave = res;

		while(res)
		{
			memcpy(dst, res->ai_addr, res->ai_addrlen);
			res = res->ai_next;
		}

		freeaddrinfo(ressave);
		return 1;
	}
#endif



bool Network::IpAddress::isIpv4Addr(const std::string& addr) const
{
	int t[4];
	
	if(sscanf(addr.c_str(), "%d.%d.%d.%d", t, t+1, t+2, t+3) == 4)
	{
		for(int i=0 ; i<4 ; i++)
			if(t[i] < 0 || t[i] > 255)
				return false;
		
		return true;
	}
	
	return false;
}



Network::IpAddress::IpAddress(const std::string& addr) throw(ArgumentException)
{
	int res;
	
	memset((void*)&_addr, 0, sizeof(_addr));
	
	if(isIpv4Addr(addr))
	{
		_addr.family = AF_INET;
		res = inet_pton(AF_INET, addr.c_str(), (void*)&_addr.v4.sin_addr);
	}
	else
	{
		_addr.family = AF_INET6;
    	res = inet_pton(AF_INET6, addr.c_str(), (void*)&_addr.v6.sin6_addr);
    }
    
    if(res <= 0)
    	throw ArgumentException("Bad IP address");
}



Network::IpAddress::IpAddress(const SOCKADDR* addr) throw(ArgumentException)
{
	if(addr == NULL)
		throw ArgumentException("Bad IP address (NULL)");
	
	if(addr->sa_family == AF_INET)
		memcpy(&_addr.v4, addr, sizeof(SOCKADDR_IN));
	else if(addr->sa_family == AF_INET6)
		memcpy(&_addr.v6, addr, sizeof(SOCKADDR_IN6));
	else
		throw ArgumentException("Bad IP address version");
}



Network::IpAddress::~IpAddress()
{
    
}



Network::IpAddress::Type Network::IpAddress::type() const
{
	if(_addr.family == AF_INET)
		return IPV4;
	else
		return IPV6;
}



string Network::IpAddress::ip() const
{
	if(_addr.family == AF_INET)
	{
		char buff[INET_ADDRSTRLEN];
    	inet_ntop(AF_INET, (void*)&_addr.v4.sin_addr, buff, sizeof(SOCKADDR_IN));
    	return string(buff);
    }
    else
    {
    	char buff[INET6_ADDRSTRLEN];
    	inet_ntop(AF_INET6, (void*)&_addr.v6.sin6_addr, buff, sizeof(SOCKADDR_IN6));
    	return string(buff);
    }
}



const SOCKADDR* Network::IpAddress::sockaddr() const
{
	return &_addr.base;
}



int Network::IpAddress::sockaddrSize() const
{
	if(_addr.family == AF_INET)
		return sizeof(SOCKADDR_IN);
	else
		return sizeof(SOCKADDR_IN6);
}



SOCKADDR* Network::IpAddress::sockaddr()
{
	return &_addr.base;
}



ostream& Network::operator<<(ostream& out, const Network::IpAddress& ipa)
{
    out << ipa.ip();
    return out;
}




